package com.tomato.spring.framework.core;

import com.tomato.spring.framework.core.annotation.Component;
import com.tomato.spring.framework.core.annotation.Controller;
import com.tomato.spring.framework.core.annotation.Repository;
import com.tomato.spring.framework.core.annotation.Service;
import com.tomato.spring.framework.util.ClassUtil;
import com.tomato.spring.framework.util.CollectionUtil;
import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Annotation;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * <pre>
 *      整体实现思路如下:
 *       1.解析配置类，例如获取某个包下面所有的Class对象
 *       2.过滤出加了com.tomato.spring.framework.core.annotaion的Class类型对象
 *       3.将过滤出来的对象进行实例化，并放入map种
 *         为了线程安全，map中同一个对象应该只存放一次
 * </pre>
 *
 * @author tomato
 */
@Slf4j
public class BeanContainer {
    /**
     * 用来存放bean对象
     * key为 bean的 Class对象
     * value 为 bean的实例化对象
     */
    private final static Map<Class<?>, Object> BEAN_MAP = new ConcurrentHashMap<>();

    /**
     * 创建bean对象的注解
     */
    private final static List<Class<? extends Annotation>> BEAN_ANNOTATION =
            Arrays.asList(Component.class, Controller.class, Service.class, Repository.class);


    /**
     * 锁对象
     */
    private static final Lock lock = new ReentrantLock();

    /**
     * 用来判断容器是否被加载过
     */
    private boolean flag = false;

    /**
     * 创建spring容器，且容器只会创建一次
     *
     * @return
     */
    public static BeanContainer getInstance() {
        return ContextHodler.HODLER.container;
    }

    public BeanContainer loadBean(String packageName) {
        // 加锁保证线程安全
        lock.lock();
        try {
            // 如果flag 为 true代表已经加载过了，则不需要加载
            // 这里不用担心线程安全问题，因为加了锁，如果没有锁就有可能在方法重复调用时存在多次加载问题从而导致map中的数据不准确
            if (flag) {
                return this;
            }
            // 加载之前先清空一下map
            clear();

            // 加载
            Set<Class<?>> classes = ClassUtil.extractPackageClass(packageName);

            if (CollectionUtil.isEmpty(classes)) {
                log.info("classes is null");
                return this;
            }

            classes.stream().filter(Objects::nonNull).filter(clazz -> {
                for (Class<? extends Annotation> anno : BEAN_ANNOTATION) {
                    if (clazz.isAnnotationPresent(anno)) {
                        return true;
                    }
                }
                return false;
            }).forEach(clazz -> {
                BEAN_MAP.put(clazz, ClassUtil.newInstance(clazz, true));
            });
            flag = true;
            return this;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 容器是否加载过了
     *
     * @return
     */
    public boolean isLoad() {
        return flag;
    }

    /**
     * 容器中对象个数
     *
     * @return
     */
    public Integer size() {
        return BEAN_MAP.size();
    }

    /**
     * 容器清空
     */
    public void clear() {
        BEAN_MAP.clear();
    }

    /**
     * 移除特定的bean
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T removeBean(Class<T> clazz) {
        Object remove = BEAN_MAP.remove(clazz);
        return (T) remove;
    }

    /**
     * 获取固定的bean
     *
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getBean(Class<T> clazz) {
        Object o = BEAN_MAP.get(clazz);
        return (T) o;
    }

    /**
     * 获取所有bean对象的Class对象
     *
     * @return
     */
    public Set<Class<?>> getClasses() {
        return BEAN_MAP.keySet();
    }

    /**
     * 获取所有的bean对象
     *
     * @return
     */
    public Set<Object> getBeans() {
        return new HashSet<>(BEAN_MAP.values());
    }

    /**
     * 根据注解所有其所有修饰的Class集合
     *
     * @param annotation
     * @return
     */
    public Set<Class<?>> getBeanByAnnotation(Class<? extends Annotation> annotation) {
        Set<Class<?>> classes = getClasses();
        if (CollectionUtil.isEmpty(classes)) {
            log.info("classes is null");
            return null;
        }
        return classes.stream()
                .filter(Objects::nonNull)
                .filter(clazz -> clazz.isAnnotationPresent(annotation))
                .collect(Collectors.toSet());
    }

    /**
     * 根据接口返回随影实现类对象
     *
     * @param clazz
     * @return
     */
    public Set<Class<?>> getBeanByInterface(Class<?> clazz) {
        Set<Class<?>> classes = getClasses();
        if (CollectionUtil.isEmpty(classes)) {
            log.info("classes is null");
            return null;
        }
        return classes.stream()
                .filter(Objects::nonNull)
                .filter(cla -> clazz.isAssignableFrom(cla))
                .collect(Collectors.toSet());
    }


    /**
     * 单例模式，使用枚举方式可以防止通过反射和反序列化直接创建容器对象
     * 保证容器创建安全
     */
    private enum ContextHodler {
        HODLER;
        private final BeanContainer container;

        ContextHodler() {
            container = new BeanContainer();
        }
    }

}
